package com.chinamobile.cmdi.dtn.stationaddress.aitools.igeo.tools;

import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Envelope;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.GeometryFactory; 
import org.locationtech.jts.geom.Polygon;
import org.locationtech.jts.geom.PrecisionModel;
import org.locationtech.jts.io.WKTReader;
import org.locationtech.jts.io.WKTWriter;
import org.geotools.api.referencing.crs.CoordinateReferenceSystem;
import org.geotools.api.referencing.operation.MathTransform;
//import org.geotools.geometry.iso.text.WKTParser;
import org.geotools.geometry.jts.JTS;
//import org.geotools.geometry.jts
import org.geotools.referencing.CRS;

//import org.geotools.referencing.CRS; 
//
//import org.opengis.referencing.FactoryException;
//import org.opengis.referencing.crs.CoordinateReferenceSystem;
//import org.opengis.referencing.operation.MathTransform; 


import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.iaz.tech.tools.common.utils.UtilTools;

/**
 * @author Zhao limin 
 * @apiNote 
 * 二维或者GIS 处理工具， 
 * 用于坐标转换
 * 
 */
public class NormalGeoTools {

    /** 
     * 日志
     */
	private final static Logger logger = (Logger) LoggerFactory.getLogger(NormalGeoTools.class);

    private final static int CIRCLE_BOUNDARY_NUMBER = 36;

    public final static String ESPG_CODE_DEFAULT = "EPSG:4326";
    public final static String ESPG_CODE_WEB = "EPSG:3857";

    /**
     * 认为double 值为Zero
     */
    private final static double DOUBLE_MIN_FOR_ZERO = 0.00000000000001;

    public static boolean doubleZero(double value){
        return Double.isNaN(value)?true: Math.abs(value) <= DOUBLE_MIN_FOR_ZERO; 
    }

    /**
     * 将GWS84 4326 WGS84 Web Me(WGS84 Web Mercator)
     * @param lon  经度
     * @param lat  维度
     * @return
     */
    public static double[] Epsg4326Toepsg3857(double lon, double lat){
        double[] result = null;
        
        Coordinate tar = null;
        try {
            //封装点,这个是通用的,也可以用POINT(y,x)
            Coordinate sour = new Coordinate(lon, lat);
            //这里要选择转换的坐标系是可以随意更换的
            CoordinateReferenceSystem source = CRS.decode(ESPG_CODE_DEFAULT);
            CoordinateReferenceSystem target = CRS.decode(ESPG_CODE_WEB);
            //建立转换,下面两个我屏掉的转换方式会报出需要3/7参数的异常
            
            MathTransform transform = CRS.findMathTransform(source, target, true);
            tar = new Coordinate();
            //转换
            JTS.transform(sour, tar, transform);
        } catch (Exception e) {
            e.printStackTrace();
            logger.error("执行坐标转换");
        }
        String[] split = (tar.toString().substring(1, tar.toString().length() - 1)).split(",");
        //经纬度精度
        DecimalFormat fm = new DecimalFormat("0.0000000");
        result = new double[2];
        result[0] = Double.valueOf(fm.format(Double.valueOf(split[0])));
        result[1] = Double.valueOf(fm.format(Double.valueOf(split[1])));
        
        return result;
    }

    /**
     * 将WEB 墨卡托坐标系转换成本地坐标系
     */
    public static double[] Epsg43857Toepsg4326(double x, double y){
        double[] result = null;
        
        Coordinate tar = null;
        try {
            //封装点,这个是通用的,也可以用POINT(y,x)
            Coordinate sour = new Coordinate(x, y);
            //这里要选择转换的坐标系是可以随意更换的
            CoordinateReferenceSystem source = CRS.decode(ESPG_CODE_WEB);
            CoordinateReferenceSystem target = CRS.decode(ESPG_CODE_DEFAULT);
            //建立转换,下面两个我屏掉的转换方式会报出需要3/7参数的异常
            
            MathTransform transform = CRS.findMathTransform(source, target, true);
            tar = new Coordinate();
            //转换
            JTS.transform(sour, tar, transform);
        } catch (Exception e) {
            e.printStackTrace();
            logger.error("执行坐标转换");
        }
        String[] split = (tar.toString().substring(1, tar.toString().length() - 1)).split(",");
        //经纬度精度
        DecimalFormat fm = new DecimalFormat("0.0000000");
        result = new double[2];
        result[0] = Double.valueOf(fm.format(Double.valueOf(split[0])));
        result[1] = Double.valueOf(fm.format(Double.valueOf(split[1])));
        
        return result;
    }

    /**
     * 根据点坐标，计算出圆形多边形，用于查询最小站间距
     * 圆形多变形，采用36边
     * 
     * 利用GPS 坐标
     * 
     */
    public static Polygon generateCircleWith3837(Double[] center, Double radius) {
        Polygon result = null;
        try{

            if( center == null ){
                throw new IllegalArgumentException("调用generateCircle 入口参数center 需要指定！");
            }

            if( center.length != 2 ){
                throw new IllegalArgumentException("调用generateCircle 入口参数center 需要指定合法值，center[0] is longitude, center[1] is latitude！");
            }

            if( doubleZero(radius)){
                throw new IllegalArgumentException("调用generateCircle radius 需要指定合法值，该值需要大于0！");
            } 
            GeometryFactory geometryFactory = new GeometryFactory(new PrecisionModel(PrecisionModel.FLOATING), 3857);

            double angle_step = 2.0 * Math.PI / (double)CIRCLE_BOUNDARY_NUMBER;
            if( doubleZero( angle_step ) ){
                throw new IllegalArgumentException("angle_step 的值过小，无法进行有效计算。");
            }

            Coordinate[] coordinates = new Coordinate[CIRCLE_BOUNDARY_NUMBER];

            for( int i = 0; i < CIRCLE_BOUNDARY_NUMBER; i++ ){
                double step_x = center[0] + radius * Math.cos( i * angle_step  );
                double step_y = center[1] + radius * Math.sin( i * angle_step  );
                coordinates[i] = new Coordinate( step_x,step_y);
            }
            
            result = geometryFactory.createPolygon(coordinates);
        } catch ( Exception e) {
            e.printStackTrace();
            logger.error("根据中心点和坐标转换成圆多边形错误：" + e.getMessage());
            result = null;
        }

        return result;
    }

    /**
     * 利用GPS 转换, 计算多边形
     * 根据Center 指定的坐标和半径生成对应的圆形多边形
     * @param center
     * @param radius
     * @return
     */
    public static Polygon generateCircleWith4326(Double[] center, Double radius) {
        Polygon result = null;
        try{

            if( center == null ){
                throw new IllegalArgumentException("调用generateCircle 入口参数center 需要指定！");
            }

            if( center.length != 2 ){
                throw new IllegalArgumentException("调用generateCircle 入口参数center 需要指定合法值，center[0] is longitude, center[1] is latitude！");
            }

            if( doubleZero(radius)){
                throw new IllegalArgumentException("调用generateCircle radius 需要指定合法值，该值需要大于0！");
            } 

            double[] webM_center = Epsg4326Toepsg3857(center[0], center[1]);
            if( (webM_center == null ) || 
                ( webM_center.length < 2)){
                throw new IllegalArgumentException("调用generateCircle radius , X:" +center[0] + ",Y:" + center[1] + "不能转成Web 墨卡托坐标！" );
            }

            GeometryFactory geometryFactory = new GeometryFactory(new PrecisionModel(PrecisionModel.FLOATING), 3857);

            double angle_step = 2.0 * Math.PI / (double)CIRCLE_BOUNDARY_NUMBER;
            if( doubleZero( angle_step ) ){
                throw new IllegalArgumentException("angle_step 的值过小，无法进行有效计算。");
            }

            Coordinate[] coordinates = new Coordinate[CIRCLE_BOUNDARY_NUMBER];

            for( int i = 0; i < CIRCLE_BOUNDARY_NUMBER; i++ ){
                double step_x = webM_center[0] + radius * Math.cos( i * angle_step  );
                double step_y = webM_center[1] + radius * Math.sin( i * angle_step  );
                double[] lonLat = Epsg43857Toepsg4326(  step_x, step_y);
                if( lonLat == null ) continue;
                if( lonLat.length < 2 ) continue;

                coordinates[i] = new Coordinate(lonLat[0], lonLat[1]);
            }
            
            result = geometryFactory.createPolygon(coordinates);
 
        } catch ( Exception e) {
            e.printStackTrace();
            logger.error("根据中心点和坐标转换成圆多边形错误：" + e.getMessage());
            result = null;
        }

        return result;
    }

    /**
     * 计算面积
     * @param geometry
     * @return
     */
    public static double calculateArea( Geometry geometry) {
        double result = Double.NaN;
        try{
            if( geometry == null ){
                throw new IllegalArgumentException("入口参数geometry需要指定，不能为空！");
            }
            geometry.setSRID(4326);


            CoordinateReferenceSystem source = CRS.decode(ESPG_CODE_DEFAULT);
            CoordinateReferenceSystem target = CRS.decode(ESPG_CODE_WEB);

            MathTransform transform = CRS.findMathTransform(source, target );
            Geometry targGeometry = JTS.transform(geometry, transform);
            if( targGeometry != null ){
                result = targGeometry.getArea();
            }
            
        } catch ( Exception e) {
            e.printStackTrace();
            logger.error("计算面积错误：" + e.getMessage());
            result = Double.NaN;
        }
 
        return result;
    }

     /**
      * 将坐标系为ESPG:4328 转换成ESPG:3857, 然后输入Envelope
      * @param geometry
      * @return
      * @throws Exception
      */
    public static Envelope calcuateGeometryExtend(Geometry geometry)  {
        Envelope result = null;

        try{
            if( geometry == null ){
                throw new IllegalArgumentException("入口参数geometry需要指定，不能为空！");
            } 

            CoordinateReferenceSystem source = CRS.decode(ESPG_CODE_DEFAULT);
            CoordinateReferenceSystem target = CRS.decode(ESPG_CODE_WEB);
            //建立转换,下面两个我屏掉的转换方式会报出需要3/7参数的异常
            
            MathTransform transform = CRS.findMathTransform(source, target, true);

            Geometry sr_geometry = JTS.transform(geometry, transform);
            if( sr_geometry != null ){
                result =  sr_geometry.getEnvelopeInternal(); 
            } 
        } catch ( Exception e) {
            e.printStackTrace();
            logger.error("计算面积错误：" + e.getMessage());
            result = null;
        }
        return result; 
    }
     
    /**
     * 根据问题区域，生成对应的栅格， 
     * @param geometry    坐标为ESPG:4326 的Geometry, 默认为Polygon
     * @param chipLength  栅格尺寸，米制
     * @return
     */
    public static Map<Integer,Map<Integer, Geometry>> splitWithChip(Geometry geometry, double chipLength) {
        Map<Integer,Map<Integer, Geometry>> result = null;
        try{
            if( geometry == null ){
                throw new IllegalArgumentException("入口参数geometry需要指定，不能为空！");
            } 

            if( doubleZero(chipLength)){
                throw new IllegalArgumentException("入口参数chipLength需要指定，不能为空！");
            }

            Envelope envelop = geometry.getEnvelopeInternal(); // calcuateGeometryExtend( geometry );
            if( envelop == null ){
                throw new IllegalArgumentException("入口参数geometry需要指定合法值，其中坐标采用ESPG:4326！");
            }

            double area_width = Math.abs( envelop.getMaxX() - envelop.getMinX() );
            double area_height = Math.abs( envelop.getMaxY() - envelop.getMinY() );

            if( doubleZero(area_width)){
                throw new IllegalArgumentException("入口参数geometry需要指定合法值，其中坐标采用ESPG:4326，计算的宽度过小！");
            }

            if( doubleZero(area_height)){
                throw new IllegalArgumentException("入口参数geometry需要指定合法值，其中坐标采用ESPG:4326，计算的高度过小！");
            }

            result = new HashMap<Integer, Map<Integer, Geometry>>();

            GeometryFactory geometryFactory = new GeometryFactory(new PrecisionModel(PrecisionModel.FLOATING), 3857);

            int rows = (int)( area_height / chipLength ) + 1;
            int cols = (int)( area_width / chipLength ) + 1;

            double half_of_width = area_width/2.0;
            double half_0f_height = area_height/2.0;

            double center_x = (envelop.getMaxX() + envelop.getMinX())/2.0;
            double center_y = (envelop.getMaxY() + envelop.getMinY())/2.0;
            
            
            double orgin_x = center_x - half_of_width;
            double orgin_y = center_y - half_0f_height;
            double cursor_x = 0.0;
            double cursor_y = 0.0;

            for( int row = 1; row <= rows; row++){
                cursor_y = orgin_y + (row -1 ) * chipLength;

                Map<Integer, Geometry> line = new HashMap<Integer,Geometry>();
                for( int col = 1; col <=cols; col++){
                    cursor_x = orgin_x + (col - 1) * chipLength;

                    Polygon gird_polygon = geometryFactory.createPolygon(
                        new Coordinate[]{
                            new Coordinate(cursor_x, cursor_y),
                            new Coordinate(cursor_x + chipLength, cursor_y),
                            new Coordinate(cursor_x + chipLength, cursor_y + chipLength),
                            new Coordinate(cursor_x , cursor_y + chipLength),
                            new Coordinate(cursor_x, cursor_y)
                        }
                    );

                    line.put(col, gird_polygon);
                }
                result.put(row, line); 
            } 
        } catch ( Exception e) {
            e.printStackTrace();
            logger.error("拆分栅格计算：" + e.getMessage());
            result = null;
        }
        return result;
    }

    /**
     * 将Text字符串转换成Geometry
     * @param text
     * @param espgcode
     * @return
     */
    public static Geometry getGeomFromText(String text, int espgcode ) {
        Geometry result = null;
        try{
            if( UtilTools.IsNull(text) ){
                throw new IllegalArgumentException("入口参数texty需要指定，不能为空！");
            } 
            
            if( espgcode < 0 ){
                throw new IllegalArgumentException("入口参数espgcodey需要指定合法值！");
            }

            GeometryFactory geometryFactory = new GeometryFactory(new PrecisionModel(PrecisionModel.FLOATING), espgcode);
            WKTReader reader = new WKTReader(geometryFactory); 
            /**
             * 将字符串转换成geometry, 用于支持访问
             */
            result = reader.read(UtilTools.Trim( text));

        } catch ( Exception e) {
            e.printStackTrace();
            logger.error("字符串转Geometry 异常：" + e.getMessage());
            result = null;
        }
        return result;
    }

    public static String toTextFromGeometry(Geometry geometry, int targetEpsg){
        String result = "";
        try{
            if( geometry == null ){
                throw new IllegalArgumentException("入口参数geometry需要指定，不能为空！");
            }   

            if( targetEpsg < 0 ){
                throw new IllegalArgumentException("入口参数targetEpsg需要指定合法值！");
            }
            Geometry geomerty_new = geometry;
            if( geometry.getSRID() != targetEpsg){

                String source_epsg_code = "EPSG:"  + String.valueOf( geometry.getSRID() );
                String target_epsg_code = "EPSG:"  + String.valueOf( targetEpsg );

                geomerty_new = transformCoordinateReferenceSystem(geometry, source_epsg_code, target_epsg_code );
            } 
            if( geomerty_new == null ) return result;
            WKTWriter writer = new WKTWriter();
            result = writer.write( geomerty_new );
            
        } catch ( Exception e) {
            e.printStackTrace();
            logger.error("toTextFromGeometry Geometry 转字符串异常：" + e.getMessage());
            result = "";
        }
        return result;
    }

    /**
     * 坐标系转换
     * @param geometry
     * @param souespg   源坐标系WKT 值，例如:"ESPG:4326"
     * @param tarespg   目标坐标系WKT值，例如"ESPG:3857"
     * @return
     */
    public static Geometry transformCoordinateReferenceSystem( Geometry geometry, String souepsg, String tarepsg){
        Geometry result = null;
        try{

            if( geometry == null ){
                throw new IllegalArgumentException("调用geometry 入口参数center 需要指定！");
            }            
            //这里要选择转换的坐标系是可以随意更换的
            CoordinateReferenceSystem source = CRS.decode(souepsg);
            CoordinateReferenceSystem target = CRS.decode(tarepsg);
            //建立转换,下面两个我屏掉的转换方式会报出需要3/7参数的异常            
            MathTransform transform = CRS.findMathTransform(source, target, true);
            /**
             * 坐标转换
             */
            result = JTS.transform(geometry, transform); 
             
        } catch ( Exception e) {
            e.printStackTrace();
            logger.error("transformCoord 调用坐标转换异常：" + e.getMessage());
            result = null;
        }
        return result;
    }
    
    /**
     * 转换坐标的值XY 的次序，仅仅用于POSTGIS
     * @param geometryString
     * @return
     */
    public static String transformCoordinatePointOrderForPostGIS( String geometryString ) {
    	String result = "";
    	try {
    		if( UtilTools.IsNull( geometryString )) {
    			throw new IllegalArgumentException("调用geometryString入口参数center 需要指定！");
    		}
    		
    		String deal_string = UtilTools.Trim(geometryString);
    		
    		StringBuffer sBuf = new StringBuffer();
    		
    		List<String> queue = new ArrayList<String>();
    		
    		for( int index = 0; index < deal_string.length(); index++ ) {
    			String char_curosr = deal_string.substring(index, index+ 1);
    			if( char_curosr.equals(")")) {
    				/**出队*/    				
    				List<String> coords_line = new ArrayList<String>();
    				boolean isNotIndex = false;
    				while( !queue.isEmpty() ) {
    					String queue_cursor = queue.remove(queue.size() -1 );
    					if( queue_cursor.equals("(")) {    						
    						queue.add(queue_cursor);
    						break;
    					}else if( !queue_cursor.equals(")")) {
    						coords_line.add(0, queue_cursor);
    					}else {
    						queue.add(queue_cursor);
    						isNotIndex = true;
    						/** 格式错误 **/
    						break;    						
    					}
    				}
    				if( isNotIndex  ) {
    					queue.add(char_curosr);
    					continue;
    				}
    				
    				StringBuffer sBuf_coord = new StringBuffer();
    				for(String char_value: coords_line) {
    					sBuf_coord.append(char_value);
    				}
    				
    				String[] coord_ones = UtilTools.splitStringAndTrims(sBuf_coord.toString(), new String[] {","} );
    				if( coord_ones.length == 0 ) {
    					queue.add(char_curosr);
    					continue;
    				} 
    				int ncount = 0;
    				sBuf_coord = new StringBuffer();
    				for(String oneCorrd: coord_ones ) {
    					if( ncount > 0 ) {
    						sBuf_coord.append(",");
    					}
    					int index_of = oneCorrd.indexOf(" ");
    					if( index_of <= 0 ) {
    						sBuf_coord.append(oneCorrd);
    					}else {
    						sBuf_coord.append(oneCorrd.substring(index_of + 1 ) );
    						sBuf_coord.append(" ");
    						sBuf_coord.append(oneCorrd.substring(0, index_of) );
    					}
    					ncount++;
    				}
    				
    				queue.add(sBuf_coord.toString());
    				queue.add(char_curosr);
    			}else {
    				queue.add(char_curosr);
    			}
    		}
    		for( String line : queue) {
    			sBuf.append(line);
    		}
    		
    		result = sBuf.toString();
    		
    	}catch(Exception e) {
    		logger.error("调用transformCoordinatePointOrderForPostGIS 异常：" + e.getMessage());
    	}
    	
    	return result;
    }
}